package com.transino.bop.controller;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.Resource;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.servlet.ModelAndView;

import com.kanas.app.viewobject.PlainTree;
import com.kanas.framework.business.BusinessHandler;
import com.kanas.framework.business.ResponseMessage;
import com.kanas.framework.support.ControllerSupport;
import com.kanas.framework.util.StringUtils;
import com.kanas.webak.config.domain.DictValue;
import com.kanas.webak.config.service.DictTypeService;
import com.transino.bop.context.support.WebAppContextUtils;
import com.transino.bop.datasource.SqlExecutorService;
import com.transino.bop.domain.Category;
import com.transino.bop.domain.Entity;
import com.transino.bop.domain.Option;
import com.transino.bop.domain.Property;
import com.transino.bop.domain.PropertyGroup;
import com.transino.bop.domain.Reference;

@Controller
@RequestMapping("/app/inspector")
public class EntityFactoryController extends ControllerSupport implements ServletContextAware {
	
	private ServletContext servletContext;
	private SqlExecutorService executorService;
	private Map<String,Object> parameterMap = null;
	private Map<String,String> markMap = null;
	private DictTypeService dictTypeService;
	
	private static final Logger logger = LoggerFactory.getLogger(EntityFactoryController.class);

	public ServletContext getServletContext() {
		return servletContext;
	}

	@Override
	public void setServletContext(ServletContext servletContext) {
		this.servletContext = servletContext;
	}
	
	@RequestMapping(value="/categories",method=RequestMethod.GET)
	public @ResponseBody List<PlainTree> getCategories() {
		List<PlainTree> response = new ArrayList<PlainTree>();
		PlainTree treeNode = new PlainTree("root","报表信息","");
		response.add(treeNode);
		List<Category> categories = WebAppContextUtils.getCatalog(servletContext).getCategories();
		for(Category c:categories){
			treeNode = new PlainTree(c.getId(), c.getName(), c.getSuperCateogry()==null?"root":c.getSuperCateogry().getId());
			response.add(treeNode);
			Collections.sort(c.getEntities());
			for(Entity e:c.getEntities()) {
				treeNode = new PlainTree(e.getId(), e.getLabel(), e.getCategoryId());
				response.add(treeNode);
			}
		}
		return response;
	}
	
	@RequestMapping(value="/{entityid}/dataview",method=RequestMethod.GET)
	public ModelAndView dataview(@PathVariable("entityid")String entityId,
			@RequestParam(value="removeflag",required=false)String removeFlag) {
		ModelAndView mv = new ModelAndView();
		mv.setViewName("modules/inspector/dataview");
		Entity entity = WebAppContextUtils.getEntity(servletContext, entityId);
		Collections.sort(entity.getProperties());
		mv.addObject("entity", entity);
		if(removeFlag!=null && removeFlag.equals("1")) {
			mv.addObject("removeflag", removeFlag);
		}
		return mv;
	}
	
	@RequestMapping(value="/{entityid}/data",method=RequestMethod.GET,produces = {"application/json;charset=UTF-8"})
	public @ResponseBody String listAll(@PathVariable("entityid")String entityId, HttpServletRequest request,
			@RequestParam(value="operator",required=false)String operator,@RequestParam(value="parameter",required=false)String parameter,
			@RequestParam(value="value",required=false)String value,@RequestParam(value="connector",required=false)String connector,
			@RequestParam(value="start",required=false)Integer page,@RequestParam(value="count",required=false)Integer rows,
			@RequestParam(value="removeflag",required=false)String removeFlag) throws UnsupportedEncodingException{
		request.setCharacterEncoding("utf-8");
		Entity entity = WebAppContextUtils.getEntity(servletContext, entityId);
		for(Property property:entity.getProperties()) {
			WebAppContextUtils.registerProperty(servletContext, property);
		}
		String sql = executorSearchSql(entity, operator, parameter, value, connector, removeFlag);
		List<Map<String,Object>> result = executorService.executSelectSql(sql, parameterMap, page, rows);
		String json = "";
		Property p;
		if(result!=null && result.size()>0) {
			for(Map<String,Object> map:result) {
				for(String key:map.keySet()) {
					p = WebAppContextUtils.getProperty(servletContext, key);
					for(Option o:p.getOptions()) {
						if(map.get(key).toString().equals(o.getId())) {
							map.put(key, o.getLabel());
						}
					}
				}
			}
			json = executorService.executSqlGetJson(result);
		}else {
			json = executorService.executSqlGetJson(new ArrayList<Map<String,Object>>());
		}
		return json;
	}
	
	public String executorSearchSql(Entity entity, String operator, String parameter, String value, String connector, String removeFlag) {
		StringBuilder sb = new StringBuilder(); 
		sb.append("select * from ").append(entity.getTable());
		parameterMap = new HashMap<String,Object>();
		if(operator==null || operator.equals("")) {
			if(removeFlag != null && !removeFlag.equals("")) {
				sb.append(" where remove_flag=:remove_flag");
				parameterMap.put("remove_flag", removeFlag);
			}
			return sb.toString();
		}
		if(markMap == null) {
			markMap = new HashMap<String,String>();
			markMap.put("equal", "=");
			markMap.put("notequal", "!=");
			markMap.put("less", "<");
			markMap.put("lessequal", "<=");
			markMap.put("larger", ">");
			markMap.put("largerequal", ">=");
			markMap.put("isempty", "is null");
			markMap.put("contains", "like");
			markMap.put("notcontains", "not like");
			markMap.put("startswith", "like");
			markMap.put("notstartswith", "not like");
			markMap.put("endswith", "like");
			markMap.put("notendswith", "not like");
			markMap.put("all", "and");
			markMap.put("any", "or");
		}
		if(connector==null || connector.equals("")) {
			sb.append(" where ").append(parameter).append(" ");
			sb.append(markMap.get(operator.toLowerCase()));
			if(value!=null && !value.equals("")) {
				sb.append(" :").append(parameter);
				if(operator.toLowerCase().contains("contains")) {
					executorParameterMap(parameter, "%"+value+"%");
				}else if(operator.toLowerCase().contains("starts")) {
					executorParameterMap(parameter, value+"%");
				}else if(operator.toLowerCase().contains("ends")) {
					executorParameterMap(parameter, "%"+value);
				}else {
					executorParameterMap(parameter, value);
				}
			}
		}else {
			String[] os = operator.split("-");
			String[] ps = parameter.split("-");
			String[] vs = value.split("-");
			sb.append(" where ");
			for(int i=0; i<os.length; i++) {
				if(i != 0) {
					sb.append(" ").append(markMap.get(connector)).append(" ");
				}
				sb.append(ps[i]).append(" ");
				sb.append(markMap.get(os[i].toLowerCase()));
				if(i<vs.length && vs[i]!=null && !vs[i].equals("")) {
					sb.append(" :").append(ps[i]);
					if(operator.toLowerCase().contains("contains")) {
						executorParameterMap(ps[i], "%"+vs[i]+"%");
					}else if(operator.toLowerCase().contains("starts")) {
						executorParameterMap(ps[i], vs[i]+"%");
					}else if(operator.toLowerCase().contains("ends")) {
						executorParameterMap(ps[i], "%"+vs[i]);
					}else {
						executorParameterMap(ps[i], vs[i]);
					}
				}
			}
		}
		if(removeFlag != null && !removeFlag.equals("")) {
			sb.append(" and remove_flag=:remove_flag");
			parameterMap.put("remove_flag", removeFlag);
		}
		return sb.toString();
	}
	
	public void executorParameterMap(String key, String value) {
		Property property = WebAppContextUtils.getProperty(servletContext, key);
		if(property == null)return;
		if(value!=null && property.getType().contains("decimal")) {
			if(property.getPoints().equals("0")) {
				parameterMap.put(key,Integer.parseInt(value));
			}else {
				parameterMap.put(key,Float.parseFloat(value));
			}
		}else {
			parameterMap.put(key,value);
		}
	}
	
	@RequestMapping(value="/{entityid}/dataviewnew",method=RequestMethod.GET)
	public ModelAndView dataviewnew(@PathVariable("entityid")String entityId,
			@RequestParam(value="dataid",required=false)String dataId,
			@RequestParam(value="dialog",required=false)String dialog) {
		ModelAndView mv = new ModelAndView();
		String prefix = "";
		if(dialog!=null && dialog.equals("1")) {
			prefix = "dialog";
			mv.setViewName("modules/inspector/dataview_edit");
		}else {
			mv.setViewName("modules/inspector/dataview_new");
		}
		Entity entity = WebAppContextUtils.getEntity(servletContext, entityId);
		for(Property property:entity.getProperties()) {
			WebAppContextUtils.registerProperty(servletContext, property);
		}
		Collections.sort(entity.getGroups());
		for(PropertyGroup pg:entity.getGroups()) {
			Collections.sort(pg.getProperties());
		}
		String suffix = "new";
		String sql;
		List<Map<String,Object>> result;
		Map<String,Object> valueMap = null;
		if(dataId!=null && !dataId.equals("")) {
			suffix = "edit";
			sql = "select * from " + entity.getTable() + " where id='" + dataId + "'";
			result = executorService.executSelectSql(sql, null, null, null);
			if(result!=null && result.size()>0) {
				valueMap = result.get(0);
			}
			mv.addObject("dataId", dataId);
			mv.addObject("valueMap", valueMap);
		}
		String validator = validator(prefix, entity, suffix);
		List<DictValue> dvs = null;
		List<Option> os = null;
		Entity refEntity;
		Reference reference;
		for(Property p:entity.getProperties()) {
			if(p.getDicType() != "") {
				os = new ArrayList<Option>();
				dvs = dictTypeService.findByTypeCode(p.getDicType());
				for(DictValue dv:dvs) {
					os.add(new Option(dv.getCode(), dv.getLabel()));
				}
				p.setOptions(os);
			}
			if(p.getType().equals("many2one")) {
				refEntity = WebAppContextUtils.getEntity(servletContext, p.getRefEntity());
				sql = "select * from " + refEntity.getTable() + " where remove_flag='0'";
				result = executorService.executSelectSql(sql, null, null, null);
				reference = p.getReferences().get(0);
				Property property = WebAppContextUtils.getProperty(servletContext, reference.getFrom());
				property.setOptionFlag(true);
				os = new ArrayList<Option>();
				for(Map<String,Object> map:result) {
					os.add(new Option(map.get(reference.getTo()).toString(),map.get(reference.getTo()).toString()));
				}
				property.setOptions(os);
			}else if(p.getType().equals("one2many")) {
				refEntity = WebAppContextUtils.getEntity(servletContext, p.getRefEntity());
				p.setReferenceEntity(refEntity);
				if(dataId!=null && !dataId.equals("")) {
					reference = p.getReferences().get(0);
					sql = "select * from " + refEntity.getTable() + " where remove_flag='0' and " + reference.getTo() + "=:" + reference.getTo();
					parameterMap = new HashMap<String,Object>();
					parameterMap.put(reference.getTo(), valueMap.get(reference.getFrom()));
					result = executorService.executSelectSql(sql, parameterMap, null, null);
					mv.addObject("one2many", result);
				}
			}
		}
		mv.addObject("entity", entity);
		mv.addObject("suffix", suffix);
		mv.addObject("validator", validator);
		return mv;
	}
	
	public String validator(String prefix, Entity e, String suffix) {
		String validator = e.getValidator();
		if(validator == "")return validator;
		Pattern p = Pattern.compile(".*?\\$\\{(.*?)\\}\\$.*?");
		Matcher m = p.matcher(validator);
		String type;
		while(m.find()) {
			type = WebAppContextUtils.getProperty(servletContext, m.group(1)).getType();
			//TODO 
			if(type!=null && type.equals("decimal")) {
				validator = validator.replace("${"+m.group(1)+"}$", "dijit.byId('"+prefix+m.group(1)+suffix+"').get('displayedValue')");
			}else {
				validator = validator.replace("${"+m.group(1)+"}$", "dijit.byId('"+prefix+m.group(1)+suffix+"').get('value')");
			}
		}
		return validator;
	}
	
	@RequestMapping(value="/{entityid}/datanew",method=RequestMethod.POST)
	public @ResponseBody ResponseMessage addNew(@PathVariable("entityid") final String entityId, 
			@RequestParam(value="dataId",required=false) final String dataId, final HttpServletRequest request) {
		ResponseMessage response = ResponseMessage.handleException(new BusinessHandler(){
			@Override
			public Object handle() throws Exception {
				String sql;
				if(dataId!=null && !dataId.equals("")) {
					sql = executorEidtSql(entityId, dataId, request);
				}else {
					sql = executorSql(entityId, request);
				}
				executorService.executSql(sql, parameterMap);
				return entityId;
			}
		}, logger);
		return response;
	}
	
	public String executorSql(String entityId, HttpServletRequest request) {
		Entity entity = WebAppContextUtils.getEntity(servletContext, entityId);
		if(entity.getTable()==null || entity.getTable()=="") {
			throw new IllegalArgumentException("The entity' table can not be null!");
		}
		StringBuilder front = new StringBuilder("insert into " + entity.getTable() + " (id,");
		StringBuilder backend = new StringBuilder(" values (:id,");
		parameterMap = new HashMap<String,Object>();
		parameterMap.put("id", StringUtils.generateUUID());
		String value = null;
		for(Object key:request.getParameterMap().keySet()) {
			value = request.getParameter(key.toString());
			if(value==null || value=="")continue;
			front.append(" " + key.toString() + ",");
			backend.append(" :" + key.toString() + ",");
			executorParameterMap(key.toString(), value);
		}
		parameterMap.put("remove_flag", "0");
		front.append(" remove_flag");
		backend.append(" :remove_flag");
		String sql = front.toString() + ")" + backend.toString() + ")";
		return sql;
	}
	
	public String executorEidtSql(String entityId, String dataId, HttpServletRequest request) {
		Entity entity = WebAppContextUtils.getEntity(servletContext, entityId);
		if(entity.getTable()==null || entity.getTable()=="") {
			throw new IllegalArgumentException("The entity' table can not be null!");
		}
		StringBuilder sb = new StringBuilder("update " + entity.getTable() + " set");
		parameterMap = new HashMap<String,Object>();
		String value = null;
		for(Object key:request.getParameterMap().keySet()) {
			value = request.getParameter(key.toString());
			if(value==null || value=="" || key.toString().equals("dataId"))continue;
			sb.append(" ").append(key.toString()).append("=:").append(key.toString()).append(",");
			executorParameterMap(key.toString(), value);
		}
		String sql = sb.toString().substring(0, sb.toString().length()-1);
		sql += " where id='" + dataId + "'";
		return sql;
	}
	
	@RequestMapping(value="/{entityid}/datadel/{dataid}")
	public @ResponseBody ResponseMessage deleteData(@PathVariable("entityid") final String entityId, 
			@PathVariable("dataid")final String dataId, final HttpServletRequest request) {
		ResponseMessage response = ResponseMessage.handleException(new BusinessHandler(){
			@Override
			public Object handle() throws Exception {
				Entity entity = WebAppContextUtils.getEntity(servletContext, entityId);
				if(entity.getTable()==null || entity.getTable()=="") {
					throw new IllegalArgumentException("The entity' table can not be null!");
				}
				String sql = "update " + entity.getTable() + " set remove_flag='1' where id='" + dataId + "'";
				executorService.executSql(sql, null);
				return entityId;
			}
		}, logger);
		return response;
	}
	
	public SqlExecutorService getExecutorService() {
		return executorService;
	}

	@Resource(name="kanas.data.service.sqlexecutor")
	public void setExecutorService(SqlExecutorService executorService) {
		this.executorService = executorService;
	}

	public DictTypeService getDictTypeService() {
		return dictTypeService;
	}

	@Resource(name="com.kanas.webak.config.service.dicttypeservice")
	public void setDictTypeService(DictTypeService dictTypeService) {
		this.dictTypeService = dictTypeService;
	}
	
}
